# ============================================================================
# 
#  MyKnowledgePortlet.py
#  KnowledgeEngine
#  
#  Created by John Meredith on 2007-02-20.
#  Copyright 2007 Faulkner Technologies. All rights reserved.
# 
# ============================================================================


# ============================================================================
# Zope Library Imports
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager
from App.ImageFile import ImageFile

# ============================================================================
# Python Library Imports

# ============================================================================
# CMF Library Imports
from Products.CMFCore import permissions
from Products.CMFCore.utils import getToolByName

# ============================================================================
# Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Portlet import Portlet, PortletInfo, addPortletSlots
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *


# ============================================================================
# Get all models contained in the portal
# ----------------------------------------------------------------------------
def getModels(context):
    """
        Return the models in the portal as [(Title,Identity)]
    """
    
    #get the portal catalog
    catalog = getToolByName(context, "knowledgeengine_genericobjectcatalogservice")
    
    #search for entity sources by their family
    searchResults = catalog(getClassName = "Model")
    
    #results aren't the actual objects, so lets reference them
    result = []
    for searchResult in searchResults:
        #attempt to resolve the actual object reference
        model = searchResult.getObject()
        
        #only add models that truely exist!
        if model is not None:
            result.append((model.getTitleOrId(), model.getIdentity()))

    # Sort the results by TitleOrId
    result.sort(lambda x, y: cmp(x[0], y[0]))

    return result


# ============================================================================
# MyKnowledgePortletInfo initialization
# ----------------------------------------------------------------------------
class MyKnowledgePortletInfo(PortletInfo):
    """
        Defines the class info methods, defining information about the class
        without needing an instance of the class
    """

    # ------------------------------------------------------------------------
    def getClassName(self):
        """
            Returns the name of the class for which
            we are providing information.
        """
        return 'MyKnowledgePortlet'


    # ------------------------------------------------------------------------
    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """

        return "MyKnowledgePortlet"


    # ------------------------------------------------------------------------
    def getDescription(self):
        """
            ABSTRACT: Return a friendly description on the purpose
            of this class.  This description is often used in the
            Property Editor for instances of this class
        """

        return "Defines a portlet containing links to model-specific MyKnowledge areas"


    # ------------------------------------------------------------------------
    def getIdentityType(self):
        """
            Return the identity type.
        """

        return "portletMyK"


    # ------------------------------------------------------------------------
    def getIcon(self, relative_to_portal=0):
        """
            PLONE Support:
            
            Returns a URL to the icon for the object
        """
                
        return self.getClassName() + '_icon.png'


    # ------------------------------------------------------------------------
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """

        return ImageFile('skins/MyKnowledgePortlet_icon.png', globals())


    # ------------------------------------------------------------------------
    def isPortalObject(self):
        """
            Returns if the object can be placed directly in 
            a Plone Portal (rather than like most objects
            that must exist within a GenericObject container
            object.
            
            eg: Models, EntitySources are Portal Objects.
        """
        
        return true


    # ------------------------------------------------------------------------
    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific properties using the PropertyReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class properties for reflection
        PortletInfo.onConfigureProperties(self)

        self.addPropertyDefinition(
            PropertyDefinition(
                identity="editModelLink",
                displayName="Include 'Edit' Link",
                description="Show an edit link next to the model listing. NOTE: This is only available for users in the 'Manager' role.",
                defaultValue=False,
                propertyType=pt_BOOLEAN,
                propertyStructure=ps_ATOMIC,
                propertyFormat=pf_NATIVE
            )
        )

        self.addPropertyDefinition(
            PropertyDefinition(
                identity="modelSelection",
                displayName="Model Selection",
                description="Classify this portlet (used for stylesheets)",
                defaultValue='Select',
                propertyType=pt_STRING,
                propertyFormat=pf_NATIVE,
                hasEnumeratedValues=true,
                enumeratedValuesMapping=[ ('Show all Models', 'All'), ('Select specific models...', 'Select') ],
                constructorEVAP=evap_READWRITE,
                propertyEditorEVAP=evap_READWRITE,
                jsInitialise            = """
                    $('modelSelection').observe(
                        'change',
                        function(event) {
                            var element = Event.element(event);
                            if (element.value == 'Select') {
                                $('displayedModelsField').show();
                            } else {
                                $('displayedModelsField').hide();
                            }
                        }
                    );
                """
            )
        )

        #self.addPropertyDefinition(
            #PropertyDefinition(
                #identity                 = "displayedModels",
                #displayName              = "Displayed Models",
                #description              = "Models which will be included in the portlet's list",
                #defaultValue             = [],
                #propertyType             = pt_NATIVE,
                #propertyStructure        = ps_SET,
                #propertyFormat           = pf_NATIVE,
                #hasEnumeratedValues      = true,
                #enumeratedValuesFunction = getModels,
                #constructorEVAP          = evap_READWRITE,
                #propertyEditorEVAP       = evap_READWRITE,
                #jsInitialise             = """
                    #if ($('modelSelection').value == 'All') {
                        #$('displayedModelsField').hide();
                    #};
                #"""
            #)
        #)
        
        def getModels(context):
            results = []
            rs = context.knowledgeengine_genericobjectcatalogservice(getClassName='Model')
            for row in rs:
                try:
                    obj = row.getObject()
                    results.append( obj )
                except AttributeError: # Catalog can't find the object
                    continue
                
            return results
                
            
        #lambda context: [brain.getObject() for brain in context.knowledgeengine_genericobjectcatalogservice(getClassName='Model')]    
                        
        self.addPropertyDefinition(PropertyDefinition(
            identity='displayedModels',
            displayName="Repositories",
            description="Select the repositories to show",
            defaultValue="",
            propertyType="L",
            getModelFunction          = getModels,
            isDisplayableFunction     = lambda object: object.getClassName() in ('Model','Repository') ,
            #isSelectableFunction      = lambda object: object.getClassName() == 'Repository',
            #isInitiallyExpandedFunction=lambda x:False,
            #nodeTitleFunction=nodeTitleFunction,
            #onClickEvent="document.getElementsByName('kedisplayFormat')[0].value += '<$' + this.value + '$>'",
            # singleSelection=true,
            propertyFormat=pf_GENERIC_OBJECT_CHOOSER,
            propertyStructure=ps_ATOMIC
        ))           
        
        

        self.addPropertyDefinition(
            PropertyDefinition(
                identity="classification",
                displayName="Classification",
                description="Classify this portlet (used for stylesheets)",
                defaultValue='activity',
                propertyType=pt_STRING,
                propertyFormat=pf_NATIVE,
                hasEnumeratedValues=true,
                enumeratedValuesMapping=[ ('Activity', 'activity'), ('Profile', 'profile') ],
                constructorEVAP=evap_READWRITE,
                propertyEditorEVAP=evap_READWRITE
            )
        )


    # ------------------------------------------------------------------------
    def onConfigureEvents(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific events using the EventReflection interface.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class events for reflection
        PortletInfo.onConfigureEvents(self)


    # ------------------------------------------------------------------------
    def onConfigurePermissions(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register
            class specific permission definitions.

            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered

            NOTE: This method is called before ClassInfo.onAfterStartup()
        """

        #initialise the super-class permissions for reflection
        PortletInfo.onConfigurePermissions(self)


# ============================================================================
# MyKnowledgePortlet initialization
# ----------------------------------------------------------------------------
class MyKnowledgePortlet(Portlet):
    """
        A Plone portlet which lists links to specific model myknowledge
        areas.
    """

    security = ClassSecurityInfo()
    security.declareObjectPublic()
    security.setDefaultAccess("allow")

    # ------------------------------------------------------------------------
    # ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info
    # about this class
    _CLASSINFO = MyKnowledgePortletInfo()

    # ------------------------------------------------------------------------
    # Default Attribute Values
    meta_type = 'MyKnowledgePortlet'


    # ------------------------------------------------------------------------
    def __init__(self, id, identity):
        """
            Constructor for the object
        """
        
        Portlet.__init__(self, id, identity)

        #perform onAfterInstanciation
        self.onAfterInstanciation()


    # ------------------------------------------------------------------------
    def getGenericObjectCatalogService(self):
        """
            Get the generic object catalog service
        """

        return getToolByName(self, 'knowledgeengine_genericobjectcatalogservice', None)


    # ------------------------------------------------------------------------
    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object
            has been loaded into memory.

            This is useful for performing backwards compatibility
        """

        #perform default onAfterInstanciation for the container
        Portlet.onAfterInstanciation(self)


    # ------------------------------------------------------------------------
    def getEditModelLink(self):
        """
            Return a BOOLEAN on whether a link to edit the model in question
            should be displayed. This only applies to 'Managers'
        """

        return getattr(self, 'editModelLink', False)


    # ------------------------------------------------------------------------
    def setEditModelLink(self, newValue):
        """
            Sets the BOOLEAN about whether a to display link to edit each
            model listed in the portlet
        """

        # Do nothing if the old and new values are identical
        if newValue == self.getEditModelLink():
            return

        self.editModelLink = newValue


    # ------------------------------------------------------------------------
    def getModelSelection(self):
        """ Returns which mode the portlet return models """

        return getattr(self, 'modelSelection', 'Select')


    # ------------------------------------------------------------------------
    def setModelSelection(self, selection):
        """  """

        # Do nothing if the old and new values are identical
        if selection == self.getModelSelection():
            return

        self.modelSelection = selection


    # ------------------------------------------------------------------------
    def getDisplayedModels(self):
        """  """

        # HACK - Slow
        # Return the list of chosen models
        results = []
        for item in ([] or self.displayedModels):
            if item.startswith('model'):
                results.append( item )
                
        return results 
            
        #return [] or [model for model in self.displayedModels if model.isInstanceOf('Model')]

    # ------------------------------------------------------------------------
    
    def getDisplayedRepositories(self, modelIdentity):
        """ Returns the sections to display for the specified model Identity """
        
        results = []
        
        # HACK - Slow
        
        # Return the list of chosen repositories for the given model
        for item in self.displayedModels:
            if item.startswith('repo'):
                repository = self.getComponentByIdentity( item )
                if repository and repository.aq_parent.getIdentity() == modelIdentity:
                    results.append( repository )
                
        return results 
        
    
    
    # ------------------------------------------------------------------------
    
    def setDisplayedModels(self, newValue):
        """  """

        # Do nothing if the old and new values are identical
        if newValue == self.getDisplayedModels():
            return

        self.displayedModels = newValue


    # ------------------------------------------------------------------------
    def getClassification(self):
        """
            Returns the classification of the portlet
        """

        return self.classification


    # ------------------------------------------------------------------------
    def setClassification(self, newValue):
        """
            Sets the classification of the portlet
        """

        # Do nothing if the old and new values are identical
        if newValue == self.getClassification():
            return

        self.classification = newValue


    # ------------------------------------------------------------------------
    def getModels(self):
        """
            Returns a list of models which were selected in displayedModels
        """

        catalog = self.getGenericObjectCatalogService()

        # If the object is set to display all model (rather than a selection)
        # get all the models
        if self.getModelSelection() == 'All':
            searchResults = catalog.searchResults(getClassName="Model")
        else:
            searchResults = catalog.searchResults(getIdentity=self.getDisplayedModels())

        return [brains.getObject() for brains in searchResults]

    # ------------------------------------------------------------------------
    def getContents(self, REQUEST, **args):
        """
            Returns the content of the specific portlet. This should be
            overridden in subclasses i.e. MyKnowledgePortlet
        """
        
        return self.MyKnowledgePortlet_template(REQUEST, portlet=self, **args)


# ============================================================================
# CMF Required Class Initialisation
# ----------------------------------------------------------------------------
def addMyKnowledgePortlet(self, id, REQUEST=None):
    """
        The Zope/CMF factory method to create an instance of a
        KnowledgeEngine MyKnowledge portlet
    """

    # Create a KnowledgeEngine identity for the new portlet
    identity = generateIdentity(MyKnowledgePortlet.identity_type)

    # Create an instance of the portlet
    portlet = MyKnowledgePortlet(id, identity)

    # Add the object to the specified container (called self)
    self._setObject(id, portlet)

    # We need the portlet slots to have a reference to the templates. Ensure
    # they are there
    # -- No longer used now, add via for Dashboard
    #addPortletSlots(self)

    if REQUEST is not None:
        REQUEST['RESPONSE'].redirect(portlet.absolute_url() + '/manage_main' )


# ============================================================================
# The CMF/Plone factory type information - used to create objects!)
# ----------------------------------------------------------------------------
factoryTypeInformation = {
    'id'             : 'MyKnowledgePortlet',
    'meta_type'      : 'MyKnowledgePortlet',
    'description'    : "MyKnowledge Portlet",
    'icon'           : 'MyKnowledgePortlet_icon.png',
    'product'        : 'KnowledgeEngine',
    'factory'        : 'addMyKnowledgePortlet',
    'filter_content_types' : 0,
    'immediate_view' : 'view',
    'actions'        : (
            {
                'id'            : 'view',
                'name'          : 'View',
                'action'        : 'string:${folder_url}/view',
                'permissions'   : (permissions.View,),
                'category'      : 'folder',
            },
        ),
}


# ============================================================================
# Register Class Information with the KnowledgeEngine
# ----------------------------------------------------------------------------
registerPortalObjectClass( MyKnowledgePortlet, addMyKnowledgePortlet, factoryTypeInformation )